home *** CD-ROM | disk | FTP | other *** search
- ////////////////////////////////////////////////////////////
- // lwtoiv.c++
- //
- // A simplistic Lightwave to Open Inventor 3D object converter
- //
- // Written by Marvin Landis
- // marvinl@amber.rc.arizona.edu
- // May 1995
- //
- // This code is placed in the public domain. It doesn't matter what
- // you do with it, but if you make any enhancements I would certainly
- // like to see them.
- //
- // There are several limitations and problems with this converter,
- // described in the Readme file which can be found in the original
- // distribution. If you don't have the original distribution,
- // e-mail me and I'll send it to you.
- //
- ////////////////////////////////////////////////////////////
-
- #include <Inventor/SoDB.h>
- #include <Inventor/SbBasic.h>
- #include <Inventor/SbString.h>
- #include <Inventor/actions/SoSearchAction.h>
- #include <Inventor/actions/SoWriteAction.h>
- #include <Inventor/nodes/SoBaseColor.h>
- #include <Inventor/nodes/SoCoordinate3.h>
- #include <Inventor/nodes/SoIndexedFaceSet.h>
- #include <Inventor/nodes/SoIndexedLineSet.h>
- #include <Inventor/nodes/SoLabel.h>
- #include <Inventor/nodes/SoMaterial.h>
- #include <Inventor/nodes/SoNormal.h>
- #include <Inventor/nodes/SoNormalBinding.h>
- #include <Inventor/nodes/SoScale.h>
- #include <Inventor/nodes/SoSeparator.h>
- #include <Inventor/nodes/SoShapeHints.h>
- #include <Inventor/SoPath.h>
- #include <sys/types.h>
-
- #define MAX_AMB 0.2;
-
- static SbName *snames;
-
- static SoSeparator *root;
- static SoScale *scale;
- static SoCoordinate3 *coords;
- static SoShapeHints *shapehints;
- static SoMaterial *materials;
- static SoIndexedFaceSet *faces;
- static SoIndexedLineSet *lines;
-
- static u_long nSrfs = 0;
- static u_long *fcnt, *lcnt;
-
-
- ////////////////////////////////////////////////////////////
- // Prints an error message to stderr and exits.
- ////////////////////////////////////////////////////////////
-
- static void error(const char *message)
- {
- fprintf(stderr, "Error: %s\n", message);
- exit(1);
- }
-
-
- /////////////////////////////////////////////////////////////
- // Reads vertices into a SoCoordinate3 node.
- // Returns the number of vertices read.
- /////////////////////////////////////////////////////////////
-
- static int read_pnts(u_long nbytes, FILE *file)
- {
- float (*pt)[3];
- u_long nPts;
-
- nPts = nbytes/sizeof(float[3]);
-
- coords = new SoCoordinate3;
- pt = new float[nPts][3];
-
- fread((void *)pt, sizeof(float), nPts * 3, file);
- coords->point.setValues(0, nPts, pt);
-
- delete pt;
- return(nPts);
- }
-
-
- /////////////////////////////////////////////////////////////
- // Reads polygons into either a SoIndexedFaceSet or a SoIndexedLineSet node.
- // Returns the number of indexes (vertex and surface) read.
- /////////////////////////////////////////////////////////////
-
- static int read_pols(u_long nbytes, FILE *file)
- {
- short *polys;
- u_long nPols;
- SbBool repeat;
- int i, j, k, cnt, snum, sc;
-
- // The Lightwave file specification states that the POLS chunk must be
- // preceded by the SRFS chunk. If not, nSrfs will be 0.
- if (nSrfs == 0)
- error("No SRFS chunk in this Lightwave file");
-
- // Not really the number of polygons. Actually the total number of
- // shorts in this chunk.
- nPols = nbytes/(sizeof(short));
- polys = new short[nPols];
- fread((void *)polys, sizeof(short), nPols, file);
-
- // Initialize the face and line counters.
- fcnt = new u_long[nSrfs];
- lcnt = new u_long[nSrfs];
- for (i = 0; i < nSrfs; i++) fcnt[i] = lcnt[i] = 0;
- faces = new SoIndexedFaceSet[nSrfs];
- lines = new SoIndexedLineSet[nSrfs];
-
- i = 0;
- while (i < nPols) {
- cnt = polys[i++];
-
- // Surface index is found here.
- sc = i + cnt;
-
- // Look for negative surface value (detail polygons follow).
- if (polys[sc] < 0)
- // Absolute value of the surface index. Advance the pointer to
- // skip over the short specifying how many detail polygons follow.
- // (I don't think I should care, since detail polygons look like
- // all other polygons as far as I'm concerned).
- snum = -polys[sc++] - 1;
- else
- // Surface indexes are numbered starting at 1.
- snum = polys[sc] - 1;
-
- // Look for repeated vertices in the polygon. Actually test the
- // coordinates, since I found some Lightwave objects in which the
- // indexes were all different, but the coordinates still repeated.
- repeat = FALSE;
- for (j = i; j < i+cnt-1; j++)
- for (k = j+1; k < i+cnt; k++)
- if (coords->point[polys[j]] == coords->point[polys[k]])
- repeat = TRUE;
-
- // If polygons have repeated vertices or have less than 3 vertices,
- // add them to an SoIndexedLineSet for that surface. Otherwise add
- // them to an SoIndexedFaceSet for that surface.
- if ((repeat) || (cnt < 3)) {
- for (j = 0; j < cnt; j++)
- lines[snum].coordIndex.set1Value(lcnt[snum]++, (long) polys[i++]);
- lines[snum].coordIndex.set1Value(lcnt[snum]++, SO_END_LINE_INDEX);
- } else {
- for (j = 0; j < cnt; j++)
- faces[snum].coordIndex.set1Value(fcnt[snum]++, (long) polys[i++]);
- faces[snum].coordIndex.set1Value(fcnt[snum]++, SO_END_FACE_INDEX);
- }
-
- // Go on to the next polygon.
- i = sc + 1;
- }
-
- delete polys;
-
- return(nPols);
- }
-
-
- /////////////////////////////////////////////////////////////
- // Reads a SURF chunk, and sets all the appropriate values for a
- // SoShapeHints and SoMaterial node. Returns the surface number.
- /////////////////////////////////////////////////////////////
-
- static u_long scnt=0;
- static int read_surf(u_long nbytes, FILE *file)
- {
- u_long ncnt = 0, bytesRead = 0;
- u_long type;
- u_short size;
- char ch, name[255];
- int i;
- char colr[4] = {200, 200, 200, 0};
- float fcolr[3];
- float fdiff = .4, fspec = 0, flumi = 0, fglos = 0, ftran = 0;
- float sman = 0;
- u_short diff = 100, spec = 0, glos = 0, lumi = 0, tran = 0;
-
- // Read the name of the surface. Make sure the characters read are
- // legal characters for a SbName type.
- ch = fgetc(file);
- bytesRead++;
- if (snames[scnt].isIdentStartChar(ch)) name[ncnt++] = ch;
- while (ch != 0) {
- ch = fgetc(file);
- bytesRead++;
- if (snames[scnt].isIdentChar(ch)) name[ncnt++] = ch;
- }
- name[ncnt] = 0;
-
- // If the length of the surface name is odd, skip another byte.
- if ((bytesRead % 2) > 0) {
- ch = fgetc(file);
- bytesRead++;
- }
-
- while (bytesRead < nbytes) {
-
- // Handle the various sub-chunks.
- fread(&type, sizeof(u_long), 1, file);
- fread(&size, sizeof(u_short), 1, file);
- switch (type) {
-
- case 'COLR': fread(&colr, sizeof(char), 4, file);
- for (i = 0; i < 3; i++)
- fcolr[i] = (float) colr[i] / 255;
- break;
-
- case 'DIFF': fread(&diff, sizeof(u_short), 1, file);
- fdiff = (float) diff / 255;
- break;
-
- case 'SPEC': fread(&spec, sizeof(u_short), 1, file);
- fspec = (float) spec / 255;
- break;
-
- case 'GLOS': fread(&glos, sizeof(u_short), 1, file);
- fglos = (float) spec / 1025;
- break;
-
- case 'LUMI': fread(&lumi, sizeof(u_short), 1, file);
- flumi = (float) lumi / 255;
- break;
-
- case 'TRAN': fread(&tran, sizeof(u_short), 1, file);
- ftran = (float) tran / 255;
- break;
-
- case 'SMAN': fread(&sman, sizeof(float), 1, file);
- break;
-
- default: fseek(file, (long)size, SEEK_CUR);
- }
-
- bytesRead += sizeof(u_long) + sizeof(u_short) + size;
- }
-
- snames[scnt] = SbName(name);
-
- // Set the fields for this surface's SoShapeHints node. Set the
- // vertexOrdering to CLOCKWISE because of the negative scaling done
- // around the z-axis (see the main program).
- shapehints[scnt].vertexOrdering = SoShapeHints::CLOCKWISE;
- shapehints[scnt].faceType = SoShapeHints::UNKNOWN_FACE_TYPE;
- shapehints[scnt].creaseAngle = sman;
-
- // Set the fields for this surface's SoMaterial node.
- materials[scnt].diffuseColor.setValue(fdiff*fcolr[0], fdiff*fcolr[1], fdiff*fcolr[2]);
- materials[scnt].specularColor.setValue(fspec*fcolr[0], fspec*fcolr[1], fspec*fcolr[2]);
- materials[scnt].emissiveColor.setValue(flumi*fcolr[0], flumi*fcolr[1], flumi*fcolr[2]);
- materials[scnt].shininess.setValue(fglos);
- materials[scnt].transparency.setValue(ftran);
- scnt++;
-
- return(scnt);
- }
-
-
- /////////////////////////////////////////////////////////////
- // Reads the SRFS chunk to determine how many surfaces are used by this
- // object. Allocates memory for all the SoMaterial and SoShapeHints nodes,
- // and the table for all the SbNames. Returns the number of surfaces.
- /////////////////////////////////////////////////////////////
-
- static int read_srfs(u_long nbytes, FILE *file)
- {
- char *names;
- int i;
-
- names = new char[nbytes];
- fread((void *)names, 1, nbytes, file);
-
- i = 0;
- while (i < nbytes) {
- i += strlen(&names[i]);
- nSrfs++;
- // Skip any extra nulls at the end of the surface name.
- while ((names[i] == '\0') && (i < nbytes)) i++;
- }
-
- shapehints = new SoShapeHints[nSrfs];
- materials = new SoMaterial[nSrfs];
- snames = new SbName[nSrfs];
-
- delete names;
- return(nSrfs);
- }
-
-
- /////////////////////////////////////////////////////////////
- // Main program.
- /////////////////////////////////////////////////////////////
-
- main(int argc, char *argv[]) {
- SoSeparator *sep1, *sep2;
- SoLabel *lab;
- FILE *file;
- u_long datasize, bytesread;
- u_long type, size;
- int i;
-
- if (argc != 2)
- error("Usage: lwtoiv file.lwob [> file.iv]");
- file = fopen(argv[1], "r");
-
- // Initialize the Inventor database.
- SoDB::init();
-
- // Make sure the Lightwave file is an IFF file.
- fread(&type, sizeof(u_long), 1, file);
- if (type != 'FORM')
- error("Not an IFF file (Missing FORM tag)");
-
- fread(&datasize, sizeof(u_long), 1, file);
-
- // Make sure the IFF file has a LWOB form type.
- fread(&type, sizeof(u_long), 1, file);
- if (type != 'LWOB')
- error("Not a lightwave object (Missing LWOB tag)");
-
- // The root node.
- root = new SoSeparator;
- root->ref();
-
- // Read all Lightwave chunks.
- bytesread = 4;
- while (bytesread < datasize) {
-
- fread(&type, sizeof(u_long), 1, file);
- fread(&size, sizeof(u_long), 1, file);
-
- switch (type) {
- case 'PNTS': read_pnts(size, file); break;
- case 'POLS': read_pols(size, file); break;
- case 'SRFS': read_srfs(size, file); break;
- case 'SURF': read_surf(size, file); break;
- default: fseek(file, (long)size, SEEK_CUR);
- }
-
- bytesread += size + 8;
-
- }
-
- // Inventor's z-axis points in the opposite direction from Lightwave.
- scale = new SoScale;
- scale->scaleFactor.setValue(1, 1, -1);
-
- // Add the SoScale and SoCoordinate3 node to the root hierarchy.
- root->addChild(scale);
- root->addChild(coords);
-
- for (i = 0; i < nSrfs; i++) {
-
- // Will always be added as a child to the root node. Will contain
- // a SoLabel node and another SoSeparator node as children.
- sep1 = new SoSeparator;
-
- // Will always be added as a child to the sep1 node. Will contain
- // SoShapeHints, SoMaterial, SoIndexedFaceSet, and SoIndexedLineSet
- // nodes as children.
- sep2 = new SoSeparator;
-
- // Add the SoShapeHints, SoMaterial, SoIndexedFaceSet, and
- // SoIndexedLineSet nodes as children to sep2.
- sep2->addChild(&shapehints[i]);
- sep2->addChild(&materials[i]);
- // If there are faces for this surface.
- if (fcnt[i] > 0)
- sep2->addChild(&faces[i]);
- // If there are lines for this surface.
- if (lcnt[i] > 0) {
- sep2->addChild(&lines[i]);
- fprintf(stderr,"WARNING: added polyline(s) to \"%s\" node\n",
- snames[i].getString());
- }
-
- // Add the SoLabel and SoSeperator nodes as children to sep1.
- lab = new SoLabel;
- lab->label.setValue(snames[i]);
- sep1->addChild(lab);
- sep1->addChild(sep2);
-
- // Add this surface's SoSeperator node as a child to root.
- root->addChild(sep1);
- sep1->setName(snames[i]);
- }
-
- // Write the entire root hierarchy to stdout.
- SoWriteAction wa;
- wa.apply(root);
-
- fprintf(stderr, "lw->iv conversion done.\n");
-
- return 0;
- }
-
-